#!/usr/bin/env python
# -*- coding: utf-8 -*-

import urllib2, cookielib
import re
import string
import time

from threading import Lock
from threading import Thread

from session import session
from ssi import ssi
from tcpconn import *
from packet_seq import sequencer
import cmd_processor as Processors
from logger import log, fytionException, Pass

FetionOnline = "400"
FetionBusy   = "600"
FetionAway   = "100"
FetionHidden  = "0"

class fytion_thread(Thread):
    def __init__(self, fytion):
        self._fytion = fytion
        Thread.__init__(self)
        
    def run(self):
        while not self._fytion.stop:
            self._fytion.tick()
            time.sleep(1.0)
        log("fytion ticker end")

class fytion(object):

    Domain = "fetion.com.cn"
    FetionVer = "4.1.1160"
    SIPCVer = "SIP-C/4.0"
    msgCallback = Pass
    exitCallback = Pass
    
    def __init__(self):
        # 0: init, 1: begin reg, 2: fin sub
        self._state = 0
        self.stop = False
        self.contacts={}
        self.sessions=[]
        self._pending_msg=[]
        self._lock = Lock()
    
    def init(self, phoneNo='', password='', proxy_info=None):
        if (not phoneNo):
            raise fytionException(-100, "init wrong arguments")
        
        cookie = urllib2.HTTPCookieProcessor(cookielib.LWPCookieJar())
        if proxy_info:
            proxy_support = urllib2.ProxyHandler(\
                {"http":proxy_info})
            opener = urllib2.build_opener(proxy_support, cookie)
        else:
            opener = urllib2.build_opener(cookie)
        urllib2.install_opener(opener)
        
        ssi_login = ssi(self, opener)
        
        self._sysconf = ssi_login.get_system_config()
        self._myconf = ssi_login.get_uri(self._sysconf['url'], phoneNo, password)
        
        log(self._myconf)
        
    def login(self, presence = '0'):
        ### TCP only!
        log("Connecting...")
        self._conn = tcpconn(self._sysconf['proxy'])
        self._seq = sequencer(self, self._conn)
        self._seq.background_tick()
        
        log("Registering...")
        self._register = Processors.register(self, self._seq, presence)
        self._register.reg_round1()
        self.stop = False
        self._state = 1
    
    def register_fin(self):
        """Callback function after registation 
        """
        s = Processors.subscribe(self, self._seq)
        s.sub()
        self._state = 2
    
    def background_tick(self):
        """Setup a thread for background ticker
        """
        t = fytion_thread(self)
        t.start()
    
    def tick(self):
        """Should be called periodically
        """
        if self._lock and not self._lock.acquire(False):
            return
        self._register.keepalive()
        for m in self._pending_msg:
            self.send_msg(m[0], pending_msg=m)
        if self._lock:
            self._lock.release()
        
    def keepbusy_seq(self, seq):
        """Send a keep-busy request to a seq
        """
        s = Processors.keepbusy(self, seq)
        s.keepbusy()
    
    def seq_conn_closed(self, e):
        """Callback when sequencer is closed
        """
        self.stop = True
        for s in self.sessions:
            s.exit()
        del self._seq
        del self._conn
        del self._register
        self._state = 0
        self.exitCallback(self, e)
    
    def get_message(self, data):
        """Callback when message arrived
        """
        self.msgCallback(self, data)
    
    def setup_session(self, conf):
        """Setup a new session
        """
        s = session(self)
        s.login(conf)
        self.sessions.append(s)
    
    def remove_session(self, session):
        """Remove a dead session
        """
        try:
            self.sessions.remove(session)
        except:
            pass
        
    def exit(self):
        """Logout fetion
        """
        self.stop = True
        self._register.bye()
    
    def get_contact(self, uri='', uid='', fid='', phone='', name=''):
        """Get contact from various params
        """
        for i, c in self.contacts.items():
            if uri and c['uri'] == uri: 
                return c
            if uid and c['user_id'] == uid: 
                return c
            if fid and c['fetion_id'] == fid: 
                return c
            if phone and c['phone'] == phone: 
                return c
            if name and c['name'] == name: 
                return c
        return None
        
    def send_msg(self, msgbody, uri='', fid='', phone='', name='', pending_msg=None):
        if pending_msg:
            contact = pending_msg[1]
        else:
            contact = self.get_contact(uri=uri, fid=fid, phone=phone, name=name)
        if not contact:
            return fytionException(-200, "Cannot find contact")
        
        # find existing session
        ss = None
        for s in self.sessions:
            if s.peer_uri == contact['uri']:
                ss = s
                break
        if ss:
            ss.send_msg(msgbody)
            if pending_msg:
                self._pending_msg.remove(pending_msg)
        elif contact['presence'] == '0':
            # send offline M message
            m = Processors.message_send(self, self._seq)
            m.send(msgbody, to=contact['uri'])
        elif not pending_msg:
            # init a new session
            s = Processors.start_chat(self, self._seq)
            s.start(contact['uri'])
            self._pending_msg.append((msgbody, contact))
            
    def send_sms(self, msgbody, uri='', fid='', phone='', name=''):
        contact = self.get_contact(uri=uri, fid=fid, phone=phone, name=name)
        if not contact:
            return fytionException(-200, "Cannot find contact")
        m = Processors.message_send(self, self._seq)
        m.send(msgbody, to=contact['uri'], sms=True)
